var tgd = tgd || {};
tgd.Contests = tgd.Contests || {};
tgd.Contests.SameGame = tgd.Contests.SameGame || {};
tgd.Contests.SameGame.Solver = tgd.Contests.SameGame.Solver || {};
tgd.Contests.SameGame.View = tgd.Contests.SameGame.View || {};

tgd.Contests.SameGame.Field = function (puzzle, onGroupSelected) {
	var _puzzle = puzzle;
	var _grid = new Array();
	var _availableGroups = [];

	for (var row = 0; row < puzzle.NumRows; ++row) {
		_grid[row] = new Array();
		for (var col = 0; col < puzzle.NumColumns; ++col) {
			_grid[row][col] = new tgd.Contests.SameGame.Block(row * puzzle.NumColumns + col, puzzle.Grid[row][col], row, col);
		}
	}

	this.__defineGetter__("NumGroups", function() { return _availableGroups.length; });
	this.__defineGetter__("HasAvailableGroup", function() { return _availableGroups.length > 0; });
	this.__defineGetter__("Grid", function() { return _grid; });
	this.__defineGetter__("Puzzle", function() { return _puzzle; });
	this.__defineGetter__("IsClear", function() { return _grid[_puzzle.NumRows - 1][0] === null; });  //todo: figure out I have any blocks left 

	function removeGroup(group) {
		for (var row = 0; row < puzzle.NumRows; ++row) {
			for (var col = 0; col < puzzle.NumColumns; ++col) {
				var block = _grid[row][col];
				if (block !== null){
					if (group.Blocks.some(function(element, index, array) { return element.ID === block.ID; } )) {
						_grid[row][col] = null;
					}
				}
			}
		}
	}

	function moveBlocks() {
		//move blocks down
		for (var col = 0; col < puzzle.NumColumns; ++col) {
			var madeMove;
			do {
				madeMove = false;
				for (var row = puzzle.NumRows - 1; row > 0; --row) {
					if (_grid[row][col] === null && _grid[row - 1][col] !== null) {
						_grid[row][col] = _grid[row - 1][col];
						_grid[row - 1][col] = null;
						if (_grid[row][col]) {
							_grid[row][col].move(row, col);
						}
						madeMove = true;
					}
				}
			} while (madeMove);
		}

		//shift columns to the left
		for (var col = 0; col < puzzle.NumColumns - 1; ++col) {
			var madeMove;
			do {
				madeMove = false;
				for (var col2 = col; col2 < puzzle.NumColumns - 1; ++col2) {
					var isEmptyColumn = true, isNextColumnEmpty = true;
					for (var row = 0; (isEmptyColumn || isNextColumnEmpty) && row < puzzle.NumRows; ++row) {
						if (_grid[row][col2] !== null) {
							isEmptyColumn = false;
						}
						if (_grid[row][col2 + 1] !== null) {
							isNextColumnEmpty = false;
						}
					}
	
					if (isEmptyColumn && !isNextColumnEmpty) {
						for (var row = 0; row < puzzle.NumRows; ++row) {
							_grid[row][col2] = _grid[row][col2 + 1];
							_grid[row][col2 + 1] = null;
							if (_grid[row][col2]) {
								_grid[row][col2].move(row, col2);
							}
						}
						madeMove = true;
					}
				}
			} while (madeMove);
		}

		setNeighbors();
	}

	function setNeighbors() {
		for (var row = 0; row < puzzle.NumRows; ++row) {
			for (var col = 0; col < puzzle.NumColumns; ++col) {
				var block = _grid[row][col];
				if (block !== null) {
					//add N, E, S, W
					block.setNeighbors(
						(row > 0) ? _grid[row - 1][col] : null,
						(col < puzzle.NumColumns - 1) ? _grid[row][col + 1] : null,
						(row < puzzle.NumRows - 1) ? _grid[row + 1][col] : null,
						(col > 0) ? _grid[row][col - 1] : null
					);
				}
			}
		}
	}

	function loadAvailableGroups() {
		_availableGroups = [];

		for (var row = 0; row < puzzle.NumRows; ++row) {
			for (var col = 0; col < puzzle.NumColumns; ++col) {
				var block = _grid[row][col];
				if (block !== null) {
					if (block.calcGroups() && block.isInGroup) {
						_availableGroups.push(block.Group);
					}
				}
			}
		}
	}

	function select(idx) {
		var selectedGroup = _availableGroups[idx];

		removeGroup(selectedGroup);
		moveBlocks();
		setNeighbors();
		loadAvailableGroups();

		onGroupSelected(selectedGroup);
	}

	this.selectMax = function() {
		var maxIndex = 0;
		var maxScore = 0;

		for (var i = 0; i < _availableGroups.length; ++i) {
			if (_availableGroups[i].Score > maxScore) {
				maxIndex = i;
				maxScore = _availableGroups[i].Score;
			}
		}

		select(maxIndex);
	}

	this.selectGroup = function(idx) {
		var selectedGroup = _availableGroups[idx];

		removeGroup(selectedGroup);
		moveBlocks();
		setNeighbors();
		loadAvailableGroups();

		onGroupSelected(selectedGroup);
	}

	this.toString = function() {
		return "Puzzle: " + _puzzle;
	}

	setNeighbors();
	loadAvailableGroups();
}
